package com.jianbo.proxy.util.lhttp;

import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.jianbo.proxy.util.khttp.HttpRequest.HttpRequestException;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class HttpRequest {

	private HttpURLConnection connection = null;

	private String requestMethod;

	private String url;

	protected HttpRequest copy(final InputStream input, final OutputStream output)
			throws IOException {
		final byte[] buffer = new byte[bufferSize];
		int read;
		while ((read = input.read(buffer)) != -1) {
			output.write(buffer, 0, read);
		}
		return HttpRequest.this;
	}

	// 发送请求
	public void sendBytes(byte[] bytes) {
		if(bytes == null) {
			return;
		}
		if(bytes.length == 0) {
			return;
		}
		URLConnection connection = getConnection();
		// 获取不到发送请求
		OutputStream os;
		try {
			connection.setDoOutput(true);
			os = connection.getOutputStream();
			os.write(bytes);
			os.flush();
			os.close();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	// 获取响应
	public byte[] bytes() throws HttpRequestException {
		final ByteArrayOutputStream output = new ByteArrayOutputStream();
		try {
			copy(responseBuffer(), output);
		} catch (IOException e) {
			throw new HttpRequestException(e);
		}
		return output.toByteArray();
	}

	public byte[] bytes(int contentLength) throws HttpRequestException {
		final ByteArrayOutputStream output = byteStream(contentLength);
		try {
			copy(responseBuffer(), output);
		} catch (IOException e) {
			throw new HttpRequestException(e);
		}
		return output.toByteArray();
	}

	protected ByteArrayOutputStream byteStream(int contentLength) {
		final int size = contentLength;
		if (size > 0)
			return new ByteArrayOutputStream(size);
		else
			return new ByteArrayOutputStream();
	}

	public HttpURLConnection getConnection() {
		if (connection == null)
			connection = createConnection();
		return connection;
	}

	static TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            // TODO Auto-generated method stub
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            // TODO Auto-generated method stub
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            // TODO Auto-generated method stub
            return null;
        }
    } };

	public static class NullHostNameVerifier implements HostnameVerifier {
        /*
         * (non-Javadoc)
         *
         * @see javax.net.ssl.HostnameVerifier#verify(java.lang.String,
         * javax.net.ssl.SSLSession)
         */
        @Override
        public boolean verify(String arg0, SSLSession arg1) {
            // TODO Auto-generated method stub
            return true;
        }
    }

	private HttpURLConnection createConnection() {
		try {
			HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
			SSLContext sc = SSLContext.getInstance("TLS");
        	sc.init(null, trustAllCerts, new SecureRandom());
        	HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

			final HttpURLConnection connection;
			connection = (HttpURLConnection) new URL(url).openConnection();
			connection.setRequestMethod(requestMethod);
			return connection;
		} catch (IOException e) {
			throw new HttpRequestException(e);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public int code() throws HttpRequestException {
		try {
			return getConnection().getResponseCode();
		} catch (IOException e) {
			throw new HttpRequestException(e);
		}
	}

	public InputStream responseStream() throws HttpRequestException {
		InputStream stream;
		if (code() < HTTP_BAD_REQUEST)
			try {
				stream = getConnection().getInputStream();
			} catch (IOException e) {
				throw new HttpRequestException(e);
			}
		else {
			stream = getConnection().getErrorStream();
			if (stream == null)
				try {
					stream = getConnection().getInputStream();
				} catch (IOException e) {

					stream = new ByteArrayInputStream(new byte[0]);
				}
		}

		return stream;

	}

	private int bufferSize = 8192;

	public BufferedInputStream responseBuffer() throws HttpRequestException {
		return new BufferedInputStream(responseStream(), bufferSize);
	}

	/**
	 * Set header name to given value
	 *
	 * @param name
	 * @param value
	 * @return this request
	 */
	public HttpRequest header(final String name, final String value) {
		getConnection().setRequestProperty(name, value);
		return this;
	}

	public HttpRequest header(Map<String, String> header) {
		if(header != null) {
			for (String name : header.keySet()) {
				String value = header.get(name);
				header(name, value);
			}
		}
		return this;
	}

	public Map<String, String> getResponseHaader() {
		connection = getConnection();
		Map<String, List<String>> fields = connection.getHeaderFields();
		Map<String, String> responseHeader = new LinkedHashMap<>();
		for(String field : fields.keySet()) {
			// if(field == null) {
			//     throw new RuntimeException("field is null" + JsonUtil.toJson(connection.getHeaderField(field)));
			// }
			if(field != null) {

				responseHeader.put(field, connection.getHeaderField(field));
			}
		}
		return responseHeader;
	}

    public void disconnect() {
		getConnection().disconnect();
    }
}
